/****************************************************************************************
 **  File: Primitives.hpp
 **  Author: Asteroth
 **  Date: 3-mag-2010
 ****************************************************************************************/

#ifndef __ZEN_PRIMITIVES_HPP__
#define __ZEN_PRIMITIVES_HPP__

namespace zen {
namespace video {

class Primitives {
public:
	static inline void plane(const glm::mat4 &transform, float xTiling = 1.0f, float yTiling = 1.0f) {
		glPushMatrix();
		glLoadMatrixf(&transform[0][0]);

		glBegin (GL_QUADS);
		zen::video::Drawing::activateColor();

		glNormal3f(0.0f, 0.0f, 1.0f);
		glTexCoord2f(-xTiling, yTiling);
		glVertex3f(-1.0f, 1.0f, 0.0f);
		glTexCoord2f(-xTiling, -yTiling);
		glVertex3f(-1.0f, -1.0f, 0.0f);
		glTexCoord2f(xTiling, -yTiling);
		glVertex3f(1.0f, -1.0f, 0.0f);
		glTexCoord2f(xTiling, yTiling);
		glVertex3f(1.0f, 1.0f, 0.0f);
		
		zen::video::Drawing::restoreColor();
		glEnd();
		glPopMatrix();
	}

	static inline void cube(const glm::mat4 &transform) {
		glPushMatrix();
		glLoadMatrixf(glm::value_ptr(transform));

		glBegin (GL_QUADS);
		zen::video::Drawing::activateColor();

		// -y
		glNormal3f(0.0f, -1.0f, 0.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);

		// +y
		glNormal3f(0.0f, 1.0f, 0.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);

		// -x
		glNormal3f(-1.0f, 0.0f, 0.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);

		// +x
		glNormal3f(1.0f, 0.0f, 0.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);

		// -z
		glNormal3f(0.0f, 0.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);

		// +z
		glNormal3f(0.0f, 0.0f, 1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);
		
		zen::video::Drawing::restoreColor();
		glEnd();
		glPopMatrix();
	}

	static inline void sphere(const glm::mat4 &transform, int sides = 12) {
		glPushMatrix();
		glLoadMatrixf(&transform[0][0]);

		glBegin (GL_QUADS);
		zen::video::Drawing::activateColor();

		float a1, a2 = 0.0;
		for (int i = 1; i <= sides; i++) {
			a1 = a2;
			a2 = (float)(_2PI / sides) * i;
			float ca1 = (float)cos(a1);
			float ca2 = (float)cos(a2);
			float sa1 = (float)sin(a1);
			float sa2 = (float)sin(a2);
			for (int j = 1; j <= sides; j++) {
				glTexCoord2f((float) i / sides, 0.0f);
				glVertex3f(ca1, sa1, 0.0f);
				glTexCoord2f((float) (i + 1) / sides, 0.0f);
				glVertex3f(ca2, sa2, 0.0f);
				glTexCoord2f((float) (i + 1) / sides, 1.0f);
				glVertex3f(ca2, sa2, 1.0f);
				glTexCoord2f((float) i / sides, 1.0f);
				glVertex3f(ca1, sa1, 1.0f);
			}
		}
		zen::video::Drawing::restoreColor();
		glEnd();
		glPopMatrix();
	}

	static inline void cylinder(const glm::mat4 &transform, int sides = 12) {
		glPushMatrix();
		glLoadMatrixf(&transform[0][0]);

		glBegin (GL_QUADS);
		zen::video::Drawing::activateColor();

		float tmp;
		float c1, s1, c2 = 0.5f, s2 = 0.0f;
		float t1, t2 = 0.0;
		for (int i = 1; i <= sides; i++) {
			c1 = c2;
			s1 = s2;
			tmp = (float) (_2PI / sides) * i;
			c2 = (float) cos(tmp) * 0.5f;
			s2 = (float) sin(tmp) * 0.5f;
			t1 = t2;
			t2 = (float) i / sides;
			glNormal3f(c1, s1, 0.0);
			glTexCoord2f(t1, 1.0);
			glVertex3f(c1, s1, 1.0);
			glTexCoord2f(t1, 0.0);
			glVertex3f(c1, s1, 0.0);
			glNormal3f(c2, s2, 0.0);
			glTexCoord2f(t2, 0.0);
			glVertex3f(c2, s2, 0.0);
			glTexCoord2f(t2, 1.0);
			glVertex3f(c2, s2, 1.0);
		}

		glEnd();
		glBegin (GL_TRIANGLES);

		c2 = 0.5f;
		s2 = 0.0f;
		t2 = 0.0;
		for (int i = 1; i <= sides; i++) {
			c1 = c2;
			s1 = s2;
			tmp = (float) (_2PI / sides) * i;
			c2 = (float) cos(tmp) * 0.5f;
			s2 = (float) sin(tmp) * 0.5f;
			t1 = t2;
			t2 = (float) i / sides;
			glNormal3f(0.0f, 0.0f, -1.0f);
			glTexCoord2f(0.5f, 0.5f);
			glVertex3f(0.0f, 0.0f, 0.0f);
			glTexCoord2f(0.5f + c2 * 0.5f, 0.5f + s2 * 0.5f);
			glVertex3f(c2, s2, 0.0);
			glTexCoord2f(0.5f + c1 * 0.5f, 0.5f + s1 * 0.5f);
			glVertex3f(c1, s1, 0.0f);
			glNormal3f(0.0f, 0.0f, 1.0f);
			glTexCoord2f(0.5f, 0.5f);
			glVertex3f(0.0, 0.0, 1.0);
			glTexCoord2f(0.5f - c1 * 0.5f, 0.5f + s1 * 0.5f);
			glVertex3f(c1, s1, 1.0);
			glTexCoord2f(0.5f - c2 * 0.5f, 0.5f + s2 * 0.5f);
			glVertex3f(c2, s2, 1.0);
		}
		
		zen::video::Drawing::restoreColor();
		glEnd();
		glPopMatrix();
	}

};

} // namespace video
} // namespace zen

#endif
